﻿using AppTemplate.Infrastructure.EntityBase;
using AppTemplate.Infrastructure.Paging;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AppTemplate.Infrastructure.ServiceBase
{
    public abstract class ServiceBase<T> : IService<T> where T : class, IEntity
    {
        private readonly DbContext dbContext;

        public ServiceBase(DbContext dbContext)
        {
            this.dbContext = dbContext;
        }

        public virtual Task<int> Create(T entity, CancellationToken cancellationToken = default)
        {
            dbContext.Set<T>().Add(entity);
            return dbContext.SaveChangesAsync(cancellationToken);

        }


        public virtual Task<int> Delete(T entity, CancellationToken cancellationToken = default)
        {
            dbContext.Set<T>().Remove(entity);
            return dbContext.SaveChangesAsync(cancellationToken);
        }
        public virtual async Task<int> Delete(int id, CancellationToken cancellationToken = default)
        {
            var entity = await this.dbContext.Set<T>().FirstOrDefaultAsync(i => i.Id == id, cancellationToken);
            if (entity != null)
            {
                this.dbContext.Set<T>().Remove(entity);
                return await this.dbContext.SaveChangesAsync(cancellationToken);
            }
            else return 0;
        }

        public async Task<int> Update(T entity, CancellationToken cancellationToken = default)
        {
            this.dbContext.Set<T>().Update(entity);
            return await dbContext.SaveChangesAsync(cancellationToken);
        }

        public async Task<int> UpdateById(int id,object dataObject, CancellationToken cancellationToken=default)
        {
            var entity =await dbContext.Set<T>().FirstOrDefaultAsync(i => i.Id == id, cancellationToken);
            if(entity != null)
            {
                dbContext.Entry(entity).CurrentValues.SetValues(dataObject);
                return await dbContext.SaveChangesAsync(cancellationToken);
            }
            else
            {
                return 0;
            }
        }

        public virtual Task<PageSearchResult<T>> PageSearch(int pageIndex, int pageSize, Expression<Func<T, bool>>? predicate = default, CancellationToken cancellationToken = default)
        {

            if (predicate == default)
            {
                predicate = (T t) => true;
            }

            return Task.Run(async () =>
            {
                IQueryable<T> query = dbContext.Set<T>().Where(predicate).OrderByDescending(i => i.Id);

                pageIndex = pageIndex <= 0 ? 1 : pageIndex;
                pageSize = pageSize <= 0 ? 20 : pageSize;
                int skip = (pageIndex - 1) * pageSize;
                query = query.Skip(skip).Take(pageSize);


                var result = await query.ToListAsync();
                var total = await dbContext.Set<T>().Where(predicate).CountAsync();


                return new PageSearchResult<T> { Data = result, Total = total };
            }, cancellationToken);
        }



        public Task<T?> Get(int id, CancellationToken cancellationToken = default)
        {
            return this.dbContext.Set<T>().FirstOrDefaultAsync(e => e.Id == id, cancellationToken);
        }

        public async Task<IEnumerable<T>?> GetByIds(IEnumerable<int> ids, CancellationToken cancellationToken = default)
        {
            var result = await this.dbContext.Set<T>().Where(i => ids.Contains(i.Id)).ToListAsync(cancellationToken);
            return result;
        }

        public async Task<IEnumerable<T>?> GetAll(CancellationToken cancellationToken = default)
        {
            var result = await this.dbContext.Set<T>().ToListAsync(cancellationToken);
            return result;
        }
    }
}
